整理一下目前我對 target-action 的理解:
讓我們先來看落落長的定義
The term target–action design paradigm refers to a kind of software architecture, where a computer program is divided into objects which dynamically establish relationships by telling each other which object they should target and what action or message to send to that target when an event occurs. This is especially useful when implementing graphical user interfaces, which are by nature event-driven. — From Wikipedia
target-action 是一種軟體架構的設計模式,將程式分成很多個 objects,這些 objects 透過告知它們應該以哪個對象為目標以及在事件發生時向該目標發送什麼動作或消息來動態建立關係 . 這在實現本質上是事件驅動的圖形用戶界面時特別有用。
Target-action is a design pattern in which an object holds the information necessary to send a message to another object when an event occurs. The stored information consists of two items of data: an action selector, which identifies the method to be invoked, and a target, which is the object to receive the message. The message sent when the event occurs is called an action message. Although the target can be any object, even a framework object, it is typically a custom controller that handles the action message in an application-specific way. — From Apple Documentation
Target-action 是一種設計模式,其中一個物件在事件發生時,保留傳送訊息給另一個物件所需的資訊。這些資訊包括兩個項目:一個動作選擇器 (action selector),用於識別要呼叫的方法,以及一個目標物件(target),用於接收訊息。當事件發生時傳送的訊息稱為動作訊息 (action message)。目標物件可以是任何物件,甚至是框架物件,但通常是處理應用程式特定方式的自訂控制器。
以上資料分別截取自維基百科以及蘋果官方文件,讓我們不要咬文嚼字,改用以下例子來理解。
以上是一段簡單的 Swift 程式碼,用 class 宣告了 Door 這個物件,並且定義了一個 method open() 後,並依此建立一個 door 實例,並呼叫方法。這段程式碼遵循了 OOP 的基本原則,透過將功能封裝在 class 裡,實現了封裝性 (Encapsulation) 這個特性。
然而若將場景拉到 iOS app 上來看,例如下圖:
以上是在 iOS 開發中最基本的功能之一,也就是讓使用者點擊某個 button 後,畫面上的 label 會隨之改變。
但看似簡單的功能,實際上涉及了:
對於開發者而言所需著重之部分應為最後一點,即決定在發生何種事件後,要實現什麼樣的邏輯,來回應使用者的行為及期待。
其餘部分,則由 iOS 替你處理,讓開發者可以專注於處理事件處理,如以下蘋果官方的示意圖:
在我們最一開始學習開發 iOS 時,如果你是從 UIKit 開始學習,通常都會從 Storyboard 拉元件開始。透過拉取一個 @IBAction function 到程式碼中,並在 function 中定義你要實作的行為。事實上這就是一種 target-action mechanism 的展現。
但我們有另外一種方法可以達到相同的效果:
根據蘋果官方定義,你可以呼叫 addTarget(_:action:for:) 此 function 來替你的元件加入特定事件。
因此我們可以改用下列方法來實作:
不論是直接拉 @IBAction 或使用 addTarget(_:action:for:) 此種方式皆可達到元件與 action 分離解耦之效。
The target object—that is, the object whose action method is called. If you specify nil, UIKit searches the responder chain for an object that responds to the specified action message and delivers the message to that object.
根據官方文檔,target 如果被設置為 nil,UIKit 會在 responder chain 中查找能夠響應指定動作消息的 object,並將 action 傳遞給該 object 進行處理。以上圖來說,我們帶入 target 的參數為 self,即所在的 ViewController。
在 iOS 中,responder chain 用於處理事件和響應使用者的操作。當事件發生時,UIKit 會沿著 responder chain 向上查找第一個能夠響應該事件的對象,並將事件傳遞給其 action 進行處理。
可以看到當 target 參數設為 nil 時,仍能正確呼叫到 sayHi() 這個 function。
Responder chain 參考示意圖
Selector 這個參數,官方列舉了三種形式:
偵測什麼樣的事件,例如我們最常使用的 .touchUpInside,其餘可 可參考官方文檔。
使用 target-action 可以讓開發者可以專注於處理事件,決定使用者流程,並可使控制元件與 action 分離,提升程式碼的可維護性。